package hh.util;

import cn.hutool.core.util.StrUtil;
import hh.util.arr.ArrUtil;
import hh.util.coll.CollUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringUtil {
    static final Pattern sPattern = Pattern.compile("\\$\\{\\}|\\$|\\{\\}|%|\\?");
    
    
    public static String[] split(String s, String... delims) {
        Map dels = CommonUtil.newHashMapKeys(delims);
        ArrayList<String> list = new ArrayList<>();
        char[] chars = s.toCharArray();
        int start = 0;
        for (int i = 0; i < s.length(); i++) {
            if (dels.containsKey(chars[i])) {
                list.add(s.substring(start, i));
                start = i + 1;
            }
        }
        return list.toArray(new String[0]);
    }
    
    /**
     * Braces means { <br>
     * e.g: <br>
     * <blockquote><pre>
     *    formatBraces("select * from {} where {} = {}" , "user", "age", 18);
     *    result:  select * from user where age = 18;
     *  </blockquote></pre>
     *
     * <blockquote><pre>
     *    formatBraces("select * from {table} where {field} = {value}" , "user", "age", 18);
     *    result:  select * from user where age = 18;
     *  </blockquote></pre>
     *
     * <br>
     * formatBraces意味着参数占位符是大括号  {}
     */
    public static String format(String messagePattern, Object... argArray) {
        return formatPlaceholder(messagePattern, "{", "}", argArray);
    }
    
    /**
     * 模拟scala的字符串插值 s"name is $name" <br>
     * java编译器不支持所以只能 format("name is $",name) <br>
     * e.g: <br>
     * <blockquote><pre>
     * 			format("My name is $","jack");
     * 			format("My name is %","jack");
     * 			format("My name is ?","jack");
     * 			format("My name is {}","jack");
     * 			format("My name is ${}","jack");
     *
     * 			return "My name is jack"; <br>
     *         </blockquote></pre>
     * <p>
     * 占位符可以是: $ % {} ${} ? 最好只有一种并且不支持{name}这种<br>
     * 如果要增加占位符种类请修改{@link #sPattern} <br>
     */
    public static String formatAll(String s, Object... args) {
        if (CommonUtil.isEmpty(args)) {
            return s;
        }
        Matcher m = sPattern.matcher(s);
        StringBuilder sb = new StringBuilder();
        int left = 0;
        int argIndex = 0;
        while (m.find()) {
            if (argIndex >= args.length) {
                break;
            }
            sb.append(s, left, m.start());
            sb.append(args[argIndex++]); // append arg
            left = m.end();
        }
        if (left != s.length()) {
            sb.append(s, left, s.length());
        }
        return sb.toString();
    }
    
    /**
     * Q(question) means ? <br>
     * formatQ意味着参数占位符是问号 ?
     */
    public static String formatQ(String messagePattern, Object... argArray) {
        return formatPlaceholder(messagePattern, "?", "?", argArray);
    }
    
    /**
     * 目的是如果我这样写   ? ? ?  不知道这三个问号代表什么，因为上下文不存在没法推断 <br>
     * 但是我这么写   ${field} ${operation} ${value}  意思就明确很多： field=value
     * <blockquote><pre>
     *     format$("select * from ${table} as t where t.${field} = 11","user","age")
     *     ==>  select * from user as t where t.age=11
     * </pre></blockquote>
     *
     * <br>
     * format$ 意味着参数占位符是 ${}
     */
    public static String format$(String messagePattern, Object... argArray) {
        return formatPlaceholder(messagePattern, "${", "}", argArray);
    }
    
    /**
     * <blockquote>
     * formatPlaceholder("select * from ? where ? ? ?", "?", "?", "user", "age", "=", "18");
     * ==> select * from user where age = 18
     * </blockquote>
     * <blockquote><pre>
     *     formatPlaceholder("select * from ${table} where ${field} ${operation} ${value}",
     *     "${", "}", "user", "age", "=", "18") ;
     *     ==> select * from user where age = 18
     * </pre></blockquote>
     *
     * @param messagePattern 带占位符的string字符串
     * @param preDelim       可以是是 ${ , abc, def, #, $之类的单个多个字符
     * @param endDelim       同上
     * @param argArray       参数列表
     */
    public static String formatPlaceholder(String messagePattern, String preDelim, String endDelim, Object... argArray) {
        if (ArrUtil.isEmpty(argArray)) return messagePattern;
        
        int preDelimLen = preDelim.length();
        int endDelimLen = endDelim.length();
        boolean preEqualEnd = preDelim.equals(endDelim);  //如果是相同的  ? & $ $$ 等pre==sufix
        
        int i = 0; // 第几个{}占位符的开始索引
        int j; // 占位符{}的索引位置
        StringBuilder sbuf = new StringBuilder(50);
        for (int l = 0; l < argArray.length; l++) {
            j = messagePattern.indexOf(preDelim, i);
            if (j == -1) { //如果没有占位符，直接返回
                if (l == 0) {
                    return messagePattern;
                }
                break;
            } else {
                // indexOf函数包含fromIndex
                int endIdx;
                if (preEqualEnd) {
                    endIdx = j;
                } else {
                    endIdx = messagePattern.indexOf(endDelim, j + preDelimLen);
                }
                
                if (endIdx == -1)
                    break; //说明只有  ${ 后面一半没有了，不把他当作参数占位
                
                int middlePreDelimIdx = messagePattern.indexOf(preDelim, j + preDelimLen);
                if (middlePreDelimIdx < endIdx && middlePreDelimIdx > j)
                    j = middlePreDelimIdx;//${ ${} 避免  跳过那个不全的占位符
                
                sbuf.append(messagePattern, i, j); //append 非参数部分. 不包含j号索引
                sbuf.append(argArray[l]); //append参数部分
                i = endIdx + endDelimLen; //下一个非占位符索引位置, 是}号索引+endDelimLen
            }
        }
        
        //最后 没有发现占位需要把尾部append进去
        if (i != messagePattern.length())
            sbuf.append(messagePattern, i, messagePattern.length());
        return sbuf.toString();
    }
    
    
    public static String spaces(int num) {
        if (num <= 0) return "";
        StringBuilder sb = new StringBuilder(8);
        for (int i = 0; i < num; i++) {
            sb.append(' ');
        }
        return sb.toString();
    }
    
    public static boolean isEmpty(String str) {
        return str == null || str.trim().length() == 0 || str.equalsIgnoreCase("null");
    }
    
    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }
    
    public static String toCamelCaseRemovePre(String str, String... pres) {
        if (pres != null) {
            for (String pre : pres) {
                if (StringUtil.isNotEmpty(pre) && str.startsWith(pre)) {
                    str = str.substring(pre.length());
                    break;
                }
            }
        }
        return toCamelCase(str);
    }
    
    public static String toCamelCaseRemovePreAndSuffix(String str, String pre, String suffix) {
        if (StringUtil.isNotEmpty(pre) && str.startsWith(pre)) {
            str = str.substring(pre.length());
        }
        if (StringUtil.isNotEmpty(suffix) && str.endsWith(suffix)) {
            str = str.substring(0, str.length() - suffix.length());
        }
        return toCamelCase(str);
    }
    
    public static String toCamelCaseRemoveSuffix(String str, String... suffies) {
        if (suffies != null) {
            for (String suff : suffies) {
                if (StringUtil.isNotEmpty(suff) && str.endsWith(suff)) {
                    str = str.substring(0, str.length() - suff.length());
                    break;
                }
            }
        }
        return toCamelCase(str);
    }
    
    
    public static String toCamelCase(String str) {
        String[] split = str.split("_");
        StringBuilder sb = new StringBuilder();
        for (String s : split) {
            if (!s.isEmpty())
                sb.append(Character.toUpperCase(s.charAt(0))).append(s.substring(1));
        }
        return sb.toString();
    }
    
    public static String toCamelCaseField(String str) {
        return lowerFist(toCamelCase(str));
    }
    
    public static String lowerFist(String name) {
        return Character.toLowerCase(name.charAt(0)) + name.substring(1);
    }
    
    public static String upperFirst(String name) {
        return Character.toUpperCase(name.charAt(0)) + name.substring(1);
    }
    
    public static String getFileName(String file) {
        if (isEmpty(file)) return file;
        int i = file.indexOf(".");
        int ii = file.lastIndexOf("/");
        if (ii == -1) ii = file.lastIndexOf("\\");
        if (ii == -1) ii = file.lastIndexOf(":");
        return file.substring(ii == -1 ? 0 : ii + 1, i == -1 ? file.length() : i);
    }
    
    public static String getSimpleTableName(String tableName) {
        if (isEmpty(tableName)) return tableName;
        int i = tableName.indexOf(".");
        return tableName.replace("`", "")
                .replace("'", "")
                .substring(i == -1 ? 0 : i + 1);
    }
    
    public static String getClassNameFromJavaCode(String code) {
        int i = code.indexOf(" class ");
        if (i == -1) i = code.indexOf(" interface ");
        
        if (i == -1) return null;
        
        int left = code.indexOf(' ', i + 1);
        int right = left;
        
        boolean start = false;
        while (right++ < 10000) {
            if (start && (code.charAt(right) == ' ' || code.charAt(right) == '{')) {
                return code.substring(left, right).replace(" ", "");
            }
            if (code.charAt(right) != ' ') {
                start = true;
            }
            
        }
        return null;
    }
    
    public static boolean containsIgnoreCase(String str, String testStr) {
        if (null == str) {
            return null == testStr;
        } else {
            return str.toLowerCase().contains(testStr.toLowerCase());
        }
    }

    public static String repeat(String s,int count) {
        if(s==null||s.length()==0)return s;
        if(count<=0) return "";
        StringBuilder sb = new StringBuilder(s.length() * count);
        for (int i = 0; i < count; i++) {
            sb.append(s);
        }
        return sb.toString();
    }

    /**
     *
     * String = 1111111100111111
     * join(str,8," ") = 11111111 00111111
     *
     */
    public static String join(String str,int len, String join) {
        int i = (str.length()-1) / len;
        StringBuilder sb = new StringBuilder(str.length() + i *join.length());
        for (int j = 0; j < i; j++) {
            sb.append(str, j * len, j * len+len).append(join);
        }
        sb.append(str,str.length() - len, str.length());
        return sb.toString();
    }

    public static<T,R> String join(List<String> list, String join) {
        return join(list, t -> t, join);
    }
    public static<T,R> String join(List<T> list, Function<T,R> columnFun, String join) {
       if(CollUtil.isEmpty(list)) return null;
       if(join==null) return null;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < list.size()-1; i++) {
            sb.append(columnFun.apply(list.get(i))).append(join);
        }
        sb.append(list.get(list.size() - 1));
        return sb.toString();
    }
}
